package com.wennn.top.security.config;

import com.wennn.top.security.filter.JWTBasicAuthFilter;
import com.wennn.top.security.filter.JWTUsernamePasswordFilter;
import com.wennn.top.security.handler.AuthDeniedHandler;
import com.wennn.top.security.handler.OutSuccessHandler;
import com.wennn.top.security.point.Http401AuthEntryPoint;
import com.wennn.top.security.provider.AuthProvider;
import com.wennn.top.security.service.SessionUserService;
import com.wennn.top.security.util.ContextUtil;
import com.wennn.top.security.util.SecurityConst;
import com.wennn.top.security.util.SwaggerAuth;
import com.wennn.top.swagger.config.Swagger2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;


@Configuration
@EnableWebSecurity
@Slf4j
//@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 需要放行的URL
     */
    private static final String[] AUTH_WHITELIST = new String[]{
            // auth url
            SecurityConst.AUTH_LOGIN_URL,
            SecurityConst.AUTH_LOGOUT_URL,
            // -- register url
            "/keep/login",
            "/user/register",
            // -- swagger ui
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/doc.html",
            "/webjars/**",
            "/favicon.ico"
            // other public endpoints of your API may be appended to this array
    };

    @Value("${ignore.urls:''}")
    private String[] ignoreUrls;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private SessionUserService sessionUserService;


    // 设置 HTTP 验证规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        OutSuccessHandler outSuccessHandler = new OutSuccessHandler(sessionUserService);

        String[] allIgnoreUrl = ArrayUtils.addAll(AUTH_WHITELIST,ignoreUrls);
        http.cors().and().csrf().disable()
//                .anonymous().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(allIgnoreUrl).permitAll()
                .anyRequest().authenticated()  // 所有请求需要身份认证
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new Http401AuthEntryPoint(""))
                .accessDeniedHandler(new AuthDeniedHandler())
                .and()
                .addFilterBefore(new JWTUsernamePasswordFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
                .addFilter(new JWTBasicAuthFilter(authenticationManager(),allIgnoreUrl))
                .formLogin()
                .loginProcessingUrl(SecurityConst.AUTH_LOGIN_URL)
                .and().logout() // 默认注销行为为logout，可以通过下面的方式来修改
                .logoutUrl(SecurityConst.AUTH_LOGOUT_URL)
                .addLogoutHandler(outSuccessHandler)
                .logoutSuccessHandler(outSuccessHandler)
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .permitAll();

    }


    // 该方法是登录的时候会进入
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自定义身份验证组件
        auth.authenticationProvider(new AuthProvider(userDetailsService, bCryptPasswordEncoder, sessionUserService));
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder;
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        if (log.isDebugEnabled()) {
            log.debug("开启跨域配置");
        }
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }

    @Bean
    @ConditionalOnBean(Swagger2.class)
    public SwaggerAuth swaggerAuth() {
        if (log.isDebugEnabled()) {
            log.debug("开启Swagger");
        }
        return new SwaggerAuth();
    }

}
